﻿using System.Globalization;
using RockBus.Logging.Interfaces;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;

namespace RockBus.DataContracts
{
    public class EvaluationResult
    {
        public EvaluationResult(ILogger logger)
        {
            this.Logger = logger;
            this.TopicInfoDictionary = new Dictionary<string, TopicInfo>();
            this.DeliveryInfoDictionary = new Dictionary<string, ICollection<DeliveryInfo>>();
            this.ReceivePortInfos = new List<ReceivePortInfo>();
        }

        private ILogger Logger { get; set; }

        /// <summary>
        /// Topic Name to TopicInfo
        /// </summary>
        public IDictionary<string, TopicInfo> TopicInfoDictionary { get; private set; }

        /// <summary>
        /// SubscriberId to DeliveryInfo collection
        /// </summary>
        public IDictionary<string, ICollection<DeliveryInfo>> DeliveryInfoDictionary { get; private set; }

        /// <summary>
        /// Collection of ReceivePorts
        /// </summary>
        public ICollection<ReceivePortInfo> ReceivePortInfos { get; private set; }

        /// <summary>
        /// Adds a DeliveryInfo to the DeliveryInfoDictionary
        /// </summary>
        /// <param name="deliveryInfo">The DeliveryInfo to add.</param>
        public void Add(DeliveryInfo deliveryInfo)
        {
            var key = deliveryInfo.SubscriptionDetail.ParentId;
            if (!this.DeliveryInfoDictionary.ContainsKey(key))
            {
                this.DeliveryInfoDictionary.Add(key, new Collection<DeliveryInfo>());
            }

            var dis = this.DeliveryInfoDictionary[key];
            if (!dis.Any((di) => di.SubscriptionDetail.Id.Equals(deliveryInfo.SubscriptionDetail.Id)))
            {
                dis.Add(deliveryInfo);
            }
        }

        private IEnumerable<DeliveryInfo> GetAllDeliveryInfos()
        {
            foreach (var dis in this.DeliveryInfoDictionary.Values)
            {
                foreach (var di in dis)
                {
                    yield return di;
                }
            }
        }

        public void Add(IList<IEvaluationElementInfoBase> infos)
        {
            foreach (var info in infos)
            {
                this.Add(info);
            }
        }

        public void Add(IEvaluationElementInfoBase info)
        {
            if (info is SubscriptionInfo)
            {
                this.AddSubscription(info as SubscriptionInfo);
            }
        }

        /// <summary>
        /// Indicates whether any Subscriptions have matched the message.
        /// </summary>
        public bool HasSubscriptions { get { return (this.DeliveryInfoDictionary.Count > 0); } }

        /// <summary>
        /// Adds a list of subscriptions to the DeliveryInfoDictionary.
        /// </summary>
        /// <param name="subscriptionList">The list of subscriptions to add.</param>
        public void AddSubscriptions(IList<SubscriptionInfo> subscriptionList)
        {
            foreach (SubscriptionInfo sci in subscriptionList)
            {
                this.AddSubscription(sci);
            }
        }

        public void AddSubscription(SubscriptionInfo sci)
        {
            if ((null == sci) || (null == sci.Detail) ||
                (sci.Detail.ExpiryDateTimeUtc <= DateTime.UtcNow) ||
                (sci.Detail.Paused))
            {
                this.Logger.LogDebug(MethodBase.GetCurrentMethod(), "Excluding subscription {0} for subscriber {1}, ExpiryDateTimeUtc {2}, Paused {3}",//, for message with action {4}",
                    sci.Detail.Id,
                    sci.SubscriberInfo.Id,
                    sci.Detail.ExpiryDateTimeUtc.HasValue ? sci.Detail.ExpiryDateTimeUtc.Value.ToString(CultureInfo.CurrentCulture) : "null",
                    sci.Detail.Paused
                    //,this.MessageWrapper.Action
                    );
                return;
            }

            this.Logger.LogDebug(MethodBase.GetCurrentMethod(), "Including subscription {0} for subscriber {1}",// for message with action {2}",
                sci.Detail.Id,
                sci.SubscriberInfo.Id
                //,this.MessageWrapper.Action
                );

            DeliveryInfo di = new DeliveryInfo(sci.Detail, sci.EndpointInfo);
            this.Add(di);
        }

        public IEnumerable<DeliveryInfo> GetDeliveryInfos()
        {
            foreach (var coll in this.DeliveryInfoDictionary.Values)
            {
                foreach (var di in coll)
                {
                    yield return di;
                }
            }
        }
    }
}